package org.atlantis.grosbeak.lang.fruit;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.lang.MatchType;
import org.atlantis.grosbeak.lang.Mirror;

/**
 * 对象实例化工厂类。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 * @param <T>
 *            实例化的对象的类型。
 */
@SuppressWarnings("unchecked")
public class MirrorFruiting<T> {

	private Mirror<T> mirror;
	private Class<T> type;
	// 把按照args中元素的类型，把args封装成一个数组。
	private Object dynaArg;
	private Fruiting<T> fruiting;
	private Object[] realArgs;
	// 是否需要对参数类型进行转换
	private boolean needCast = true;

	public MirrorFruiting(Mirror<T> mirror, boolean needCast, Object... args)
			throws FruitingException {
		this.mirror = mirror;
		this.type = mirror.getMirrorType();
		this.needCast = needCast;
		if (null == args)
			args = new Object[0];
		dynaArg = Mirror.evalArgToArray(args);
		if (args.length == 0) {
			handleEmptyArgs(args);
		} else {
			handleArgs(args);
		}
		if (null == fruiting)
			throw Exceptions.makeThrow(FruitingException.class,
					"Fail to instantiate '%s'.", type.getName());
	}

	/**
	 * 生成一个实例。
	 * 
	 * @return 新生成的一个实例。
	 */
	public T fruit() {
		try {
			return fruiting.fruit(realArgs);
		} catch (Exception e) {
			throw Exceptions.makeThrow(FruitingException.class, e,
					"Fail to instantiate '%s'", type.getName());
		}
	}

	public Fruiting<T> getFruiting() {
		return fruiting;
	}

	/*
	 * 处理没有参数的情况。
	 */
	private void handleEmptyArgs(Object[] args) {
		try {
			// 首先查找无参构造方法。
			fruiting = new DefaultConstructorFruiting(type.getConstructor());
			realArgs = new Object[0];
		} catch (Exception e) {
			// 然后再查找无参工厂方法。
			Method[] sms = mirror.getStaticMethods();
			for (Method sm : sms) {
				if (sm.getReturnType() == type
						&& sm.getParameterTypes().length == 0) {
					fruiting = new DefaultFactoryMethodFruiting(sm);
					realArgs = new Object[0];
					return;
				}
			}
		}
	}

	/*
	 * 处理有参数的情况。
	 */
	private void handleArgs(Object[] args) {
		// 首先构造方法
		for (Constructor<?> c : type.getConstructors()) {
			Class<?>[] pts = c.getParameterTypes();
			MatchType match = Mirror.matchParamTypes(pts, args);
			if (match == MatchType.YES) {
				// 如果完全匹配的
				fruiting = new ConstructorFruiting(c);
				realArgs = args;
				return;
			} else if (match == MatchType.MAYBE) {
				if (needCast) {
					// 如果需要类型转换
					fruiting = new ConstructorFruiting(c);
					realArgs = Mirror.batchCast(pts, args);
				}
				return;

			} else if (null != dynaArg && pts.length == 1
					&& pts[0] == dynaArg.getClass()) {
				// 如果不匹配，但是构造方法需要的是一个动态参数（或一个数组），且动态参数（或数组）中元素的类型和dynaArg的类型相同
				fruiting = new ArgsConstructorFruiting(c);
				realArgs = args;
				return;
			}
		}

		// 然后再找工厂方法
		Method[] sms = mirror.getStaticMethods();
		for (Method sm : sms) {
			Class<?>[] pts = sm.getParameterTypes();
			MatchType match = Mirror.matchParamTypes(pts, args);
			if (match == MatchType.YES) {
				// 如果完全匹配
				fruiting = new FactoryMethodFruiting(sm);
				realArgs = args;
				return;
			} else if (match == MatchType.MAYBE) {
				if (needCast) {
					// 如果需要类型转换
					fruiting = new FactoryMethodFruiting(sm);
					realArgs = Mirror.batchCast(pts, args);
				}
				return;

			} else if (null != dynaArg && pts.length == 1
					&& pts[0] == dynaArg.getClass()) {
				// 如果不匹配，但是工厂方法需要的是一个动态参数（或一个数组），且动态参数（或数组）中元素的类型和dynaArg的类型相同
				fruiting = new ArgsFactoryMethodFruiting(sm);
				realArgs = args;
				return;
			}
		}
	}
}
